package co.edu.unal.bioingenium.kbmed.text.representation.vo;

import gnu.trove.map.TObjectIntMap;
import gnu.trove.map.hash.TObjectIntHashMap;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 *
 * @author Alejandro Riveros Cruz Riveros Cruz
 */
public class Corpus implements Serializable {

    //SerialUID
    private static final long serialVersionUID = -6403283132874380047L;
    /**
     *The corpus name
     */
    private String name;
    /**
     *The list of documents in the corpus
     */
    private Map<String, Document> documentList;
    /**
     *The word list of the corpus, the count is used to store the occurrences of the word in the whole corpus
     */
    private TObjectIntMap<String> wordCount;
    /**
     *The number of documents in which a word appear
     */
    private TObjectIntMap<String> documentCount;
    /**
     *A alphabetical sorted version of the word list
     */
    private List<String> sortedWordList;
    /**
     *A alphabetical sorted version of the document list
     */
    private List<String> sortedDocumentList;
    /**
     *A inverted version of the alphabetical sorted word list used for cross index with sparsed feature vectors
     */
    private TObjectIntMap<String> invertedSortedWordList;
    /**
     *A inverted version of the alphabetical sorted document list used for build small inverted index
     */
    private TObjectIntMap<String> invertedSortedDocumentList;
    /**
     *The average document length
     */
    private float averageDocumentLength = -1;
    /**
     *Details about the corpus construction
     */
    private String constructionDetails;

    /**
     *
     * @param name 
     */
    public Corpus(String name) {
        this.name = name;
        wordCount = new TObjectIntHashMap<String>();
        documentList = new HashMap<String, Document>();
        documentCount = new TObjectIntHashMap<String>();
        averageDocumentLength = 0;
    }

    /**
     *
     * @param documentName
     * @return  
     */
    public Document getDocument(String documentName) {
        if (documentList.containsKey(documentName)) {
            return documentList.get(documentName);
        }
        return null;
    }

    /**
     *
     * @param documentId 
     * @return 
     */
    public Document getDocumentById(Integer documentId) {
        return getDocument(getSortedDocumentList().get(documentId));
    }

    /**
     *
     * @param document 
     */
    public void addDocument(Document document) {
        if (!documentList.containsKey(document.getName())) {
            documentList.put(document.getName(), document);
        }
    }

    /**
     *
     * @param document
     * @return  
     */
    public boolean containsDocument(Document document) {
        return documentList.containsKey(document.getName());
    }

    /**
     *
     * @param documentName 
     * @return 
     */
    public boolean containsDocument(String documentName) {
        return documentList.containsKey(documentName);
    }

    /**
     *
     * @param document 
     */
    public void removeDocument(Document document) {
        documentList.remove(document.getName());
    }

    /**
     *
     * @param documentName 
     */
    public void removeDocument(String documentName) {
        documentList.remove(documentName);
    }

    /**
     * @return the numDocuments
     */
    public int getTotalDocuments() {
        return documentList.size();
    }

    /**
     *
     */
    private void calculateAverageDocumentLength() {
        long total = 0;
        for (String documentId : documentList.keySet()) {
            total += documentList.get(documentId).getTotalWords();
        }
        averageDocumentLength = (float) total / (float) documentList.size();
    }

    /**
     *
     * @param word 
     */
    public void addWordOccurrence(String word) {
        if (wordCount.containsKey(word)) {
            wordCount.increment(word);
        } else {
            wordCount.put(word, 1);
        }
    }

    /**
     * @param:
     * @return:
     * @param word 
     */
    public void addDocumentOccurence(String word) {
        if (documentCount.containsKey(word)) {
            documentCount.increment(word);
        } else {
            documentCount.put(word, 1);
        }
    }

    /**
     *
     * @param word 
     * @return 
     */
    public boolean containsWord(String word) {
        return wordCount.containsKey(word);
    }

    /**
     *
     * @param word 
     */
    public void removeWord(String word) {
        wordCount.remove(word);
    }

    /**
     * Returns the number of documents in which the given word appears
     * @param word 
     * @return 
     */
    public int getDocumentOcurrences(String word) {
        return documentCount.get(word);
    }

    /**
     * Returns the number of times that the given word appears in the whole collection
     * @param word 
     * @return 
     */
    public int getWordOcurrences(String word) {
        return wordCount.get(word);
    }

    /**
     *
     * @return 
     */
    public int getTotalWords() {
        return wordCount.size();
    }

    /**
     * @return the name
     */
    public String getName() {
        return name;
    }

    /**
     * @param name the name to set
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * @return the documentList
     */
    public Map<String, Document> getDocumentCollection() {
        return documentList;
    }

    /**
     * @param documentList the documentList to set
     */
    public void setDocumentCollection(Map<String, Document> documentList) {
        this.documentList = documentList;
    }

    /**
     * @return the wordList
     */
    public TObjectIntMap<String> getWordCount() {
        return wordCount;
    }

    /**
     *
     * @param wordList 
     */
    public void setWordCount(TObjectIntMap<String> wordList) {
        this.wordCount = wordList;
    }

    /**
     * @return the averageDocumentLength
     */
    public float getAverageDocumentLength() {
        if (averageDocumentLength == -1) {
            calculateAverageDocumentLength();
        }
        return averageDocumentLength;
    }

    /**
     * @return 
     */
    public TObjectIntMap<String> getInvertedSortedWordList() {
        if (invertedSortedWordList != null && invertedSortedWordList.size() == wordCount.size()) {
            return invertedSortedWordList;
        } else {
            getSortedWordList();
            invertedSortedWordList = new TObjectIntHashMap<String>();
            for (int wordIndex = 0; wordIndex < sortedWordList.size(); wordIndex++) {
                invertedSortedWordList.put(sortedWordList.get(wordIndex), wordIndex);
            }
            return invertedSortedWordList;
        }
    }

    /**
     * 
     * @return
     */
    public TObjectIntMap<String> getInvertedSortedDocumentList() {
        if (invertedSortedDocumentList != null && invertedSortedDocumentList.size() == documentList.size()) {
            return invertedSortedDocumentList;
        } else {
            getSortedDocumentList();
            invertedSortedDocumentList = new TObjectIntHashMap<String>();
            for (int documentIndex = 0; documentIndex < sortedDocumentList.size(); documentIndex++) {
                invertedSortedDocumentList.put(sortedDocumentList.get(documentIndex), documentIndex);
            }
            return invertedSortedDocumentList;
        }
    }

    /**
     * @return 
     */
    public List<String> getSortedWordList() {
        if (sortedWordList != null && sortedWordList.size() == wordCount.size()) {
            return sortedWordList;
        } else {
            sortedWordList = new ArrayList<String>(wordCount.keySet());
            Collections.sort(sortedWordList);
            return sortedWordList;
        }
    }

    /**
     * 
     * @return
     */
    public List<String> getSortedDocumentList() {
        if (sortedDocumentList != null && sortedDocumentList.size() == documentList.size()) {
            return sortedDocumentList;
        } else {
            sortedDocumentList = new ArrayList<String>(documentList.keySet());
            Collections.sort(sortedDocumentList);
            return sortedDocumentList;
        }
    }

    /**
     * 
     * @return
     */
    public String getConstructionDetails() {
        return constructionDetails;
    }

    /**
     * 
     * @param constructionDetails
     */
    public void setConstructionDetails(String constructionDetails) {
        this.constructionDetails = constructionDetails;
    }
}
